package com.mm.exception;

import com.mm.enums.ResultCodeEnum;
import com.mm.utils.ApiResult;
import org.apache.commons.lang3.StringUtils;
import org.apache.shiro.ShiroException;
import org.apache.shiro.authz.AuthorizationException;
import org.springframework.http.HttpStatus;
import org.springframework.validation.ObjectError;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import org.springframework.web.method.annotation.MethodArgumentTypeMismatchException;

import javax.servlet.http.HttpServletRequest;
import javax.validation.ConstraintViolation;
import javax.validation.ConstraintViolationException;
import java.util.List;
import java.util.Set;

/**
 * 统一异常处理
 */
@RestControllerAdvice
public class GlobalException {


    /**
     * 未定义异常
     * @param e
     * @return
     */
    @ExceptionHandler({Exception.class})
    public ApiResult handleIndexOutOfBoundsException(Exception e) {
        return ApiResult.fail(ResultCodeEnum.SERVER_BUSY);
    }

    /**
     * 参数类型不匹配
     */
    @ExceptionHandler({MethodArgumentTypeMismatchException.class})
    @ResponseStatus(HttpStatus.NOT_FOUND)
    public ApiResult requestTypeMismatch(MethodArgumentTypeMismatchException e){
        return ApiResult.fail(ResultCodeEnum.BODY_NOT_MATCH.getCode(), "参数类型不匹配，参数"+e.getName()+"类型必须为"+e.getRequiredType());
    }


    /**
     * JSON方式参数校验
     * @param ex
     * @param request
     * @return
     */
    @ExceptionHandler({MethodArgumentNotValidException.class})
    public ApiResult methodDtoNotValidException(Exception ex, HttpServletRequest request) {
        MethodArgumentNotValidException c = (MethodArgumentNotValidException) ex;
        List<ObjectError> errors = c.getBindingResult().getAllErrors();
        StringBuffer errorMsg = new StringBuffer();
        errors.stream().forEach(x -> {
            errorMsg.append(x.getDefaultMessage()).append(";");
        });
        return ApiResult.fail(ResultCodeEnum.BODY_NOT_MATCH.getCode(),errorMsg.toString());
    }


    /**
     * 表单方式参数校验
     * @param cve
     * @param httpServletRequest
     * @return
     */
    @ExceptionHandler(ConstraintViolationException.class)
    public ApiResult methodArgNotValidException(ConstraintViolationException cve, HttpServletRequest httpServletRequest) {
        Set<ConstraintViolation<?>> cves = cve.getConstraintViolations();
        StringBuffer errorMsg = new StringBuffer();
        cves.forEach(ex -> errorMsg.append(ex.getMessage()));
        return ApiResult.fail(ResultCodeEnum.BODY_NOT_MATCH.getCode(),errorMsg.toString());
    }

    /**
     * 处理空指针的异常
     * @param req
     * @param e
     * @return
     */
    @ExceptionHandler(value =NullPointerException.class)
    @ResponseBody
    public ApiResult exceptionHandler(HttpServletRequest req, NullPointerException e){
        System.out.println("发生空指针异常！原因是:" + e);
        return ApiResult.fail(ResultCodeEnum.BODY_NOT_MATCH);
    }

    /**
     * 类型转换异常
     */
    @ExceptionHandler(ClassCastException.class)
    public ApiResult classCastExceptionHandler(ClassCastException e) {
        e.printStackTrace();
        StackTraceElement[] elements = e.getStackTrace();
        String message = StringUtils.EMPTY;
        if(elements.length > 0){
            StackTraceElement element = elements[0];
            message = StringUtils.join("控制器", element.getClassName(), ".", element.getMethodName(), "类的第", element.getLineNumber(), "行发生", e.toString(), "异常");
        }
        if(StringUtils.isBlank(message)){
            message = e.toString();
        }
        return ApiResult.fail(ResultCodeEnum.BODY_NOT_MATCH.getCode(), message);
    }

    /**
     * 自定义业务异常处理
     * @param e
     * @return
     */
    @ExceptionHandler({BusinessException.class})
    public ApiResult businessException(BusinessException e) {
        return ApiResult.fail(e.getCode(),e.getMessage());
    }

    /**
     * shiro异常处理
     * @param e
     * @return
     */
    @ExceptionHandler(ShiroException.class)
    public ApiResult shiroException(ShiroException e) {
        return ApiResult.fail(-1,e.getMessage());
    }

    @ExceptionHandler(AuthorizationException.class)
    public ApiResult Authorization(AuthorizationException e) {
        return ApiResult.fail(ResultCodeEnum.UNAUTHORIZED);
    }
}
